package com.ldl.distributelock.zk;

import com.baomidou.mybatisplus.extension.api.R;
import org.apache.commons.lang3.StringUtils;
import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;
import org.springframework.util.CollectionUtils;

import java.util.Collections;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.stream.Collectors;

public class ZkDistributedLock implements Lock {

    private String lockName;
    private String currentNodePath;
    private ZooKeeper zooKeeper;
    //统一往这个路径下放锁
    private static final String ROOT_PATH = "/locks";
    //记录重入信息
    private static final ThreadLocal<Integer> THREAD_LOCAL = new ThreadLocal<>();

    public ZkDistributedLock(String lockName, ZooKeeper zooKeeper) {
        this.lockName = lockName;
        this.zooKeeper = zooKeeper;
        try {
            if (zooKeeper.exists(ROOT_PATH, false) == null) {
                zooKeeper.create(ROOT_PATH, null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
            }
        } catch (KeeperException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void lock() {
        tryLock();
    }

    @Override
    public void lockInterruptibly() throws InterruptedException {

    }

    @Override
    public boolean tryLock() {
        // 通过创造一个节点加锁
        try {
            Integer flag = THREAD_LOCAL.get();
            if (flag!=null&&flag>0){
                THREAD_LOCAL.set(flag+1);
                return true;
            }

            //创建失败会报错，为了防止服务宕机导致锁释放不了所以选择了临时锁，但是为了防止自旋锁浪费性能加入序列锁
            currentNodePath = zooKeeper.create(ROOT_PATH + "/" + lockName + "-", null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
            //获取前置节点，如果前置节点为空，则获取锁成功，否则监听前置节点
            String preNode = getPreNode();
            if (preNode != null) {
                // 利用闭锁思想 实现阻塞功能
                CountDownLatch countDownLatch = new CountDownLatch(1);
                // 因为获取前置节点这个操作不具有原子性 因此需要再次判断前一个节点是否存在  exists是有原子性的方式
                // 因为如果!=null  就监听上了，如果==null返回true了
               if ( zooKeeper.exists(ROOT_PATH + "/"  + preNode, new Watcher() {
                   @Override
                   public void process(WatchedEvent watchedEvent) {
                        countDownLatch.countDown();
                   }
               })==null){
                   THREAD_LOCAL.set(1);
                   return true;
               }
               countDownLatch.await();
            }
            THREAD_LOCAL.set(1);
            return true;
        } catch (Exception e) {
//            try {
//                Thread.sleep(80);
//                this.tryLock();
//            } catch (InterruptedException e1) {
//                e1.printStackTrace();
//            }
            e.printStackTrace();
        }
        return false;
    }

    private String getPreNode() {
        try {
            List<String> children = zooKeeper.getChildren(ROOT_PATH, false);
            //这里因为我们刚插进去一个临时的序列化锁  不可能为空
            if (CollectionUtils.isEmpty(children)) {
                throw new IllegalMonitorStateException("非法操作 ！children");
            }

            //获取和当前资源同一类型的锁  children返回的路径是以lockx-开头的
            List<String> nodes = children.stream().filter(node -> StringUtils.startsWith(node,lockName + "-")).collect(Collectors.toList());
            if (CollectionUtils.isEmpty(nodes)) {
                throw new IllegalMonitorStateException("非法操作 ！nodes");
            }
            // 排序后可以二分查找  可以从小到大获取前一个
            Collections.sort(nodes);

            String path = StringUtils.substringAfterLast(currentNodePath, "/");
            int index = Collections.binarySearch(children, path);
            if (index < 0) {
                throw new IllegalMonitorStateException("非法操作 ！index");
            } else if (index > 0) {
                return nodes.get(index - 1);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (KeeperException e) {
            e.printStackTrace();
        }
        //剩下的情况是index==0 该节点是第一个节点直接返回null
        return null;
    }

    @Override
    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
        return false;
    }

    @Override
    public void unlock() {
        // 通过删除一个节点解锁
        try {
            THREAD_LOCAL.set(THREAD_LOCAL.get()-1);
            if (THREAD_LOCAL.get()==0){
                zooKeeper.delete(currentNodePath, -1);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (KeeperException e) {
            e.printStackTrace();
        }
    }

    @Override
    public Condition newCondition() {
        return null;
    }
}
